<template>
	<scroll-view :scroll-y="true" :style="{height: props.height+5+'rpx'}">
	<view class="flex flex-col relative">
		<zh-sheet v-if="!props.hideTool" :shadow="0" :round="0" :height="88" :margin="[0, 0]" :padding="[0, 0]"
			_class="flex flex-col">
			<view class="flex flex-row flex-row-center-center" style="height: 88rpx">
				<view @click.stop="prevYear" class="px-16">
					<tm-icon :userInteractionEnabled="false" :font-size="22" name="tmicon-angle-double-left"></tm-icon>
				</view>
				<view @click.stop="prevMonth" class="px-16">
					<tm-icon :userInteractionEnabled="false" :font-size="22" name="tmicon-angle-left"></tm-icon>
				</view>
				<view class="px-12">
					<zh-text :userInteractionEnabled="false" _class="text-weight-b" :font-size="32"
						:label="_nowDate"></zh-text>
				</view>
				<view @click.stop="nextMonth" class="px-16">
					<tm-icon :userInteractionEnabled="false" :font-size="22" name="tmicon-angle-right"></tm-icon>
				</view>
				<view @click.stop="nextYear" class="px-16">
					<tm-icon :userInteractionEnabled="false" :font-size="22" name="tmicon-angle-double-right"></tm-icon>
				</view>
			</view>
			<view @click="nowWeekClick"
				class="absolute t-0 r--6 zIndex-10 round-12 py-4 flex flex-row flex-row-center-center"
				style="width: 90rpx; height: 88rpx">
				<zh-text :userInteractionEnabled="false" color="grey" _class="text-align-center" :font-size="28"
					:label="props.textUnit[8]"></zh-text>
			</view>
		</zh-sheet>

		<view class="flex flex-row flex-row-center-center py-12" :style="[{ height: '74rpx' }]">
			<view class="flex-1 flex-center" v-for="(item, index) in weekStr" :key="index">
				<view style="width: 62rpx" class="flex-center flex-col">
					<zh-text :font-size="24" :label="item"></zh-text>
				</view>
			</view>
		</view>
		<view class="flex flex-col">
			<view class="flex flex-row flex-row-center-center" :style="[{ height: '90rpx' }]"
				v-for="(item, index) in _data" :key="index">
				<view @click="clickWeek(item2)" class="flex-1 flex flex-row flex-row-center-center"
					v-for="(item2, index2) in item" :key="index2">
					<view :userInteractionEnabled="false" :class="[
							'flex-1',
							startOrAnd(item2.dateStr) == 1 ? 'round-l-24' : '',
							startOrAnd(item2.dateStr) == 2 ? 'round-r-24' : '',
							startOrAnd(item2.dateStr) == 3 ? 'round-24' : '',
							!startOrAnd(item2.dateStr) ? 'round-24' : ''
						]" style="height: 90rpx; overflow: hidden">
						<zh-sheet :height="90" :shadow="0" :round="0"
							:border="item2.extra.color && isSelected(item2.dateStr) ? 1 : 0" _class="flex-row"
							:isDisabledRoundAndriod="true" :paren-class="
								(startOrAnd(item2.dateStr) == 1 ? 'round-l-24' : '') +
								(startOrAnd(item2.dateStr) == 2 ? 'round-r-24' : '') +
								(startOrAnd(item2.dateStr) == 3 ? 'round-24' : '')
							" :text="
								startOrAnd(item2.dateStr) == 1 || startOrAnd(item2.dateStr) == 2 || startOrAnd(item2.dateStr) == 3
									? false
									: item2.extra.text !== null
									? item2.extra.text
									: isSelected(item2.dateStr)
							" :color="item2.extra.color ? item2.extra.color : isSelected(item2.dateStr) ? _color : 'white'" :margin="[0, 0]"
							:padding="[0, 0]">
							<view :userInteractionEnabled="false" style="width: 90rpx; height: 90rpx"
								:class="[!item2.isNowIn ? 'opacity-6' : '']" class="flex-1 flex-center">
								<view style="width: 90rpx" class="flex-center flex-col"
									:style="[{ opacity: item2.disabled ? '0.3' : '1' }]">
									<zh-text :font-size="28" :label="item2.date"></zh-text>
									<zh-text
										v-if="(startOrAnd(item2.dateStr) == 0 || startOrAnd(item2.dateStr) == 4) && item2.extra.extra"
										_class="flex-center" :font-size="22" :label="item2.extra.extra"></zh-text>
									<zh-text v-if="startOrAnd(item2.dateStr) == 1" _class="flex-center" :font-size="22"
										label="开始"></zh-text>
									<zh-text v-if="startOrAnd(item2.dateStr) == 2" _class="flex-center" :font-size="22"
										label="结束"></zh-text>
									<zh-text v-if="startOrAnd(item2.dateStr) == 3" _class="flex-center" :font-size="20"
										label="始/止"></zh-text>
								</view>
							</view>
						</zh-sheet>
					</view>
				</view>
			</view>
		</view>

		<zh-button v-if="!props.hideButton" :linear="props.linear" :linear-deep="props.linearDeep" :color="props.color"
			@click="confirm" block :label="_confirmText" :margin="[32, 16]"></zh-button>
	</view>
</scroll-view>
</template>

<script lang="ts" setup>
	/**
 * 按范围选择的日历。
 * @description 一次只能范围选择一个。
 */
	import { computed, ref, Ref, watch, PropType, toRaw, nextTick } from 'vue'
	import * as dayjs from 'dayjs/esm/index';
	import isoWeek from 'dayjs/esm/plugin/isoWeek/index';
	import isSameOrBefore from 'dayjs/esm/plugin/isSameOrBefore/index';
	import isBetween from 'dayjs/esm/plugin/isBetween/index';
	import minMax from 'dayjs/esm/plugin/minMax/index';
	import { monthDayItem, dateItemStyle } from './interface';
	import systemConfigStore from '@/store/modules/systemConfigStore';
	const store = systemConfigStore();
	/**
	 * 事件说明
	 * v-model 绑定当前的周次时间开始和结束时间。
	 * confirm 当点击确认时触发
	 * click-day 日期被选中时触发，注意禁用的日期不会触发 。
	 * change 当切换年或者月的时候触发。
	 */
	const emits = defineEmits(['update:modelValue', 'confirm', 'click-day', 'change'])
	const props = defineProps({
		followTheme: {
			type: Boolean,
			default: true
		},
		//默认显示的日期
		defaultValue: {
			type: Array as PropType<Array<String | Number | Date>>,
			default: () => []
		},
		//当前的周时间段
		modelValue: {
			type: Array as PropType<Array<String | Number | Date>>,
			default: () => []
		},
		color: {
			type: String,
			default: 'primary'
		},
		linear: {
			type: String,
			default: ''
		},
		linearDeep: {
			type: String,
			default: 'light'
		},
		//指的是：有效的可选时间，小于此时间，不允许选中。
		start: {
			type: [String, Number, Date],
			default: ''
		},
		//指的是：有效的可选时间，大于此时间，不允许选中。
		end: {
			type: [String, Number, Date],
			default: ''
		},
		//被禁用的日期数组。如果["2022-1-1","2022-5-1"]
		//被禁用的日期将无法选中。
		disabledDate: {
			type: Array as PropType<Array<String | Number | Date>>,
			default: () => []
		},
		//设定单个日期的样式。
		dateStyle: {
			type: Array as PropType<Array<dateItemStyle>>,
			default: () => []
		},
		//隐藏头部操作栏
		hideTool: {
			type: Boolean,
			default: false
		},
		//隐藏底部确认按钮
		hideButton: {
			type: Boolean,
			default: false
		},
		confirmText: {
			type: String,
			default: '确认'
		},
		height: {
			type: Number,
			default: 600
		},
		//周次，本日、本季、本年、本月、本周的文字请按顺序提供文本，方便定义其它语言。
		textUnit: {
			type: Array as PropType<string[]>,
			default: ['周次', '一', '二', '三', '四', '五', '六', '日', '本日', '本周', '本月', '本季度', '本年', '月', '第${x}季度', '年']
		}
	})
	const _color = computed(() => {
		if (props.followTheme && store.color) return store.color
		return props.color
	})
	const _confirmText = computed(() => props.confirmText)
	const DayJs = dayjs.default
	DayJs.extend(isoWeek)
	DayJs.extend(isSameOrBefore)
	DayJs.extend(isBetween)
	DayJs.extend(minMax)
	//当前选中的时间数组。
	const _value = ref(props.defaultValue)
	const weekStr = props.textUnit.slice(1, 8)
	//当前需要展示的年月。
	const showOpenDate : Ref<dayjs.Dayjs> = ref(setShowopenDate())
	//当前月历中的日期。
	const _data : Ref<Array<Array<monthDayItem>>> = ref([])
	//可选中开始时间
	const _start_date = computed(() => {
		let isv = DayJs(props.start).isValid()
		return isv ? DayJs(props.start) : DayJs('1980-1-1')
	})
	//可选中的结束时间
	const _end_date = computed(() => {
		let isv = DayJs(props.end).isValid()
		return isv ? DayJs(props.end) : DayJs('2450-1-1')
	})

	//当前展示的年月日期。
	const _nowDate = computed(() => {
		return showOpenDate.value.format('YYYY-MM')
	})
	_data.value = getWeekOfMonthArray()

	watch(
		[() => props.modelValue, () => props.dateStyle, () => props.disabledDate, () => props.start, () => props.end],
		() => {
			_value.value = props.modelValue
			showOpenDate.value = setShowopenDate()
			_data.value = getWeekOfMonthArray()
		},
		{ deep: true }
	)

	//设置当前需要展示的月份。
	function setShowopenDate() {
		//从当前选中的第一个日期开始。如果没有就显示当前本地时间的月。

		if (_value.value.length == 0) {
			return DayJs()
		}
		let n = _value.value[0] || DayJs()
		n = typeof n == 'undefined' || n == null ? DayJs() : n
		return DayJs(n)
	}

	function nowWeekClick() {
		if (isDisabledDate(DayJs())) {
			uni.showToast({ title: '无法选中', icon: 'none' })
			return
		}
		selected(DayJs().format('YYYY-MM-DD'))
		showOpenDate.value = DayJs()
		_data.value = getWeekOfMonthArray()
		emits('click-day', DayJs().format('YYYY-MM-DD'))
	}
	function clickWeek(wk : monthDayItem) {
		if (wk.disabled) {
			uni.showToast({ title: '无法选中', icon: 'none' })
			return
		}
		selected(wk.dateStr)
		emits('click-day', wk.dateStr)
	}
	//选中日期。相同需要减去。重复需要去除。
	function selected(item : string | dayjs.Dayjs | number) {
		let nowvalue = [...toRaw(_value.value)]
		if (nowvalue.length < 2) {
			nowvalue.push(DayJs(item).format('YYYY-MM-DD'))
		} else {
			nowvalue = [DayJs(item).format('YYYY-MM-DD')]
		}

		if (nowvalue.length == 2) {
			let dToDayjs = [DayJs(nowvalue[0]), DayJs(nowvalue[1])]
			_value.value = [DayJs.min(dToDayjs), DayJs.max(dToDayjs).format('YYYY-MM-DD')]
		} else {
			_value.value = nowvalue
		}
	}
	function nextYear() {
		showOpenDate.value = showOpenDate.value.add(1, 'year')
		let dys = getWeekOfMonthArray()
		emits('change', showOpenDate.value.format('YYYY-MM-DD'))
		nextTick(() => {
			_data.value = [...dys]
		})
	}
	function nextMonth() {
		showOpenDate.value = showOpenDate.value.add(1, 'month')
		let dys = getWeekOfMonthArray()
		emits('change', showOpenDate.value.format('YYYY-MM-DD'))
		nextTick(() => {
			_data.value = [...dys]
		})
	}
	function prevMonth() {
		showOpenDate.value = showOpenDate.value.subtract(1, 'month')
		let dys = getWeekOfMonthArray()
		emits('change', showOpenDate.value.format('YYYY-MM-DD'))
		nextTick(() => {
			_data.value = [...dys]
		})
	}
	function prevYear() {
		showOpenDate.value = showOpenDate.value.subtract(1, 'year')
		let dys = getWeekOfMonthArray()
		emits('change', showOpenDate.value.format('YYYY-MM-DD'))
		nextTick(() => {
			_data.value = [...dys]
		})
	}
	//设置当前选中的日期范围，参数必须为有效的时间数组。
	function setDefault(data : Array<String | Number | Date> = []) {
		_value.value = props.modelValue
		showOpenDate.value = setShowopenDate()
		_data.value = getWeekOfMonthArray()
	}
	//获取本月按周分组的时间段。
	function getWeekOfMonthArray() {
		let nowMonth : dayjs.Dayjs = showOpenDate.value.date(1)
		let startStatickDay = nowMonth.startOf('month')
		let endStatickDay = nowMonth.endOf('month')

		///当前月份有多少天。
		let nowMonthDayNum = nowMonth.daysInMonth()
		//第一个日期需要补齐1-7天的。
		let startOfday = startStatickDay.isoWeekday() - 1

		startStatickDay = nowMonth.subtract(Math.abs(startOfday), 'day')
		//最后一个日期也要补齐1-7天。
		let endOfday = 7 - endStatickDay.isoWeekday()
		if (endOfday > 0) {
			endStatickDay = nowMonth.date(nowMonthDayNum).add(Math.abs(endOfday), 'day')
		}

		let startd = DayJs(startStatickDay)
		let arOfmonth : Array<number> = [] //本月的周次。
		let ar : Array<monthDayItem> = []
		function setAr() {
			let dy = props.dateStyle.map((el) => {
				el.date = DayJs(el.date).format('YYYY-MM-DD')
				return el
			})
			let dyObj = {}
			dy.forEach((el) => {
				dyObj[el.date] = el
			})
			let dySet = Object.keys(dyObj)
			while (startd.isSameOrBefore(endStatickDay)) {
				let idate = startd.format('YYYY-MM-DD')
				let ext = dySet.includes(idate) ? dyObj[idate] : {}
				ar.push({
					dateStr: idate,
					date: startd.date() < 10 ? '0' + startd.date() : startd.date(),
					day: startd.isoWeekday(),
					week: startd.isoWeek(),
					isNowIn: isInNowMonth(nowMonth, startd),
					disabled: isDisabledDate(startd),
					extra: {
						date: idate,
						text: null,
						color: '',
						extra: '',
						...ext
					}
				})
				arOfmonth.push(startd.isoWeek())
				startd = startd.add(1, 'day')
			}
		}
		setAr()
		// 再检查当前展示时段内有没有满足6*7 42天没有也要补上剩余的天数。
		if (ar.length < 42) {
			let chaJi = 42 - ar.length
			endStatickDay = endStatickDay.add(chaJi, 'day')
			setAr()
		}

		arOfmonth = [...new Set(arOfmonth)]
		let dArray : Array<Array<monthDayItem>> = []
		let index = 0
		dArray.push([])
		ar.forEach((el) => {
			if (el.week == arOfmonth[index]) {
				dArray[index].push(el)
			} else {
				index += 1
				dArray.push([])
				dArray[index].push(el)
			}
		})
		return dArray
	}
	// 检测now日期是否在某个日期的月份中.
	function isInNowMonth(date : string | dayjs.Dayjs | number = '', now : string | dayjs.Dayjs | number = '') {
		let startStatickDay = DayJs(date).startOf('month').format('YYYY-MM-DD')
		let endStatickDay = DayJs(date).endOf('month').format('YYYY-MM-DD')
		return DayJs(now).isBetween(startStatickDay, endStatickDay, 'day', '[]')
	}
	//检测某个日期，是否在禁用中。
	function isDisabledDate(date : string | dayjs.Dayjs | number = '') {
		let valdate = DayJs(date)
		let isds = false
		isds = !valdate.isBetween(_start_date.value, _end_date.value, 'day', '[]')
		for (let i = 0; i < props.disabledDate.length; i++) {
			let item = props.disabledDate[i]
			if (DayJs(item).isSame(valdate, 'day')) {
				isds = true
				break
			}
		}
		return isds
	}
	//检测 某个日期是否被选中。
	function isSelected(date : string | dayjs.Dayjs | number = '') {
		let isSelected = false
		let fr = _value.value.filter((el) => DayJs(el).isSame(date))
		isSelected = fr.length > 0 ? true : false
		if (_value.value.length == 2) {
			isSelected = DayJs(date).isBetween(_value.value[0], _value.value[1], 'day', '[]')
		}
		return isSelected
	}
	//检测一个日期是否是开始和结束。
	function startOrAnd(date : string | dayjs.Dayjs | number = '') {
		//返回1为开始，2为结束,3开始和结束相同，0为非开始和结束以及之间。4为开始和结束之间
		if (_value.value.length == 1) {
			if (DayJs(date).isSame(_value.value[0], 'day')) {
				return 1
			}
		} else if (_value.value.length == 2) {
			if (DayJs(_value.value[1]).isSame(_value.value[0], 'day') && DayJs(date).isSame(_value.value[0], 'day')) {
				return 3
			}
			if (DayJs(date).isSame(_value.value[0], 'day')) {
				return 1
			}
			if (DayJs(date).isSame(_value.value[1], 'day')) {
				return 2
			}
			if (DayJs(date).isBetween(_value.value[0], _value.value[1], 'day', '()')) {
				return 4
			}
		}

		return 0
	}
	function confirm() {
		let ar = _value.value.map((el) => DayJs(el).format('YYYY-MM-DD'))
		emits('update:modelValue', ar)
		emits('confirm', ar)
	}
	function getValue() {
		return _value.value.map((el) => DayJs(el).format('YYYY-MM-DD'));
	}
	defineExpose({
		setDefault: setDefault,
		nextYear: nextYear,
		nextMonth: nextMonth,
		prevYear: prevYear,
		prevMonth: prevMonth,
		getValue: getValue,
	})
</script>

<style></style>